#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <stdint.h>
#include <stdbool.h>
#include <signal.h>
#include <time.h>

#include "infra_tiny_block.h"
#include "infra_u_list_priv.h"

// #define PUSH_INSERT_GIVEBACK_CB

int32_t __infra_list_giveback(infra_list_ctx_t *plist_ctx, infra_list_node_t *pnode)
{
    INFRA_PTR_CHECK(plist_ctx);
    INFRA_PTR_CHECK(pnode);

    infra_dl_list_del_init(&pnode->list);

    if (plist_ctx->_pfn_giveback_cb) {
        plist_ctx->_pfn_giveback_cb(plist_ctx->_pgb_userdata, pnode->data);
    }

    infra_tb_giveback(plist_ctx->_tb_handle, pnode);
    plist_ctx->_data_count--;
    plist_ctx->_out_count++;

    return 0;
}

int32_t __infra_list_cond_wait(infra_list_ctx_t *plist_ctx)
{
    INFRA_PTR_CHECK(plist_ctx);

    pthread_mutex_lock(&plist_ctx->_lock);
    pthread_cond_wait(&plist_ctx->_cond, &plist_ctx->_lock);
    pthread_mutex_unlock(&plist_ctx->_lock);

    return 0;
}

int32_t __infra_list_cond_wait_timeout(infra_list_ctx_t *plist_ctx, int32_t time_out)
{
    INFRA_PTR_CHECK(plist_ctx);
    struct timespec timeout = {0};

    clock_gettime(CLOCK_MONOTONIC, &timeout);
    timeout.tv_nsec += (time_out % 1000) * 1000000;
    timeout.tv_sec += time_out / 1000;
    if (timeout.tv_nsec >= 1000000000) {
        timeout.tv_nsec -= 1000000000;
        timeout.tv_sec += 1;
    }
    pthread_mutex_lock(&plist_ctx->_lock);
    pthread_cond_timedwait(&plist_ctx->_cond, &plist_ctx->_lock, &timeout);
    pthread_mutex_unlock(&plist_ctx->_lock);

    return 0;
}

infra_list_handle infra_list_create(uint32_t depth, uint32_t max_size, bool is_block,
                                    const char *token)
{
    int32_t idx = 0;
    infra_list_ctx_t *plist_ctx = NULL;

    if (NULL == token) {
        infra_err("token invalid\n");
        return NULL;
    }

    if (depth > max_size) {
        infra_err("[%8s] list max_size:%d, depth:%d invalid\n", token, max_size, depth);
        return NULL;
    }

    plist_ctx = (infra_list_ctx_t *)malloc(sizeof(infra_list_ctx_t));
    if (NULL == plist_ctx) {
        infra_err("[%8s] malloc list ctx failed, %s\n", token, strerror(errno));
        return NULL;
    } else {
        memset(plist_ctx, 0, sizeof(infra_list_ctx_t));
    }

    plist_ctx->_tb_handle = infra_tb_create(max_size, sizeof(infra_list_node_t), true, "list_tb");
    if (NULL == plist_ctx->_tb_handle) {
        infra_err("[%8s] malloc list tb handle failed, %s\n", token, strerror(errno));
        goto FAIL0;
    }

    strncpy(plist_ctx->_token, token, INFRA_TOKEN_MAX_SIZE);
    INFRA_INIT_LIST_HEAD(&plist_ctx->_list);
    plist_ctx->_depth = depth;
    plist_ctx->_max_size = max_size;
    plist_ctx->_is_block = is_block;

    pthread_mutexattr_init(&plist_ctx->_lock_attr);
    pthread_mutex_init(&plist_ctx->_lock, &plist_ctx->_lock_attr);

    if (is_block) {
        pthread_condattr_init(&plist_ctx->_cond_attr);
        pthread_condattr_setclock(&plist_ctx->_cond_attr, CLOCK_MONOTONIC);
        pthread_cond_init(&plist_ctx->_cond, &plist_ctx->_cond_attr);
    }

    return (infra_tb_handle)plist_ctx;

FAIL0:
    if (NULL != plist_ctx) {
        free(plist_ctx);
        plist_ctx = NULL;
    }

    return NULL;
}

int32_t infra_list_destroy(infra_list_handle handle)
{
    int32_t ret = 0;
    infra_list_ctx_t *plist_ctx = NULL;
    INFRA_PTR_CHECK(handle);

    plist_ctx = (infra_list_ctx_t *)handle;

    ret = infra_list_cleanup(handle);
    if (ret) {
        infra_err("[%8s] list cleanup failed, ret:%d\n", plist_ctx->_token, ret);
    }

    if (plist_ctx->_is_block) {
        pthread_cond_destroy(&plist_ctx->_cond);
        pthread_condattr_destroy(&plist_ctx->_cond_attr);
    }

    pthread_mutex_destroy(&plist_ctx->_lock);
    pthread_mutexattr_destroy(&plist_ctx->_lock_attr);

    ret = infra_tb_destroy(plist_ctx->_tb_handle);
    if (ret) {
        infra_err("[%8s] list tb destroy failed, ret:%d\n", plist_ctx->_token, ret);
    }

    free(plist_ctx);
    plist_ctx = NULL;

    return 0;
}

int32_t infra_list_cleanup(infra_list_handle handle)
{
    int32_t ret = 0;
    infra_list_ctx_t *plist_ctx = NULL;
    infra_list_node_t *pnode = NULL;
    INFRA_PTR_CHECK(handle);

    plist_ctx = (infra_list_ctx_t *)handle;

    pthread_mutex_lock(&plist_ctx->_lock);
    while (plist_ctx->_data_count > 0) {
        pnode = infra_dl_list_first_entry(&plist_ctx->_list, infra_list_node_t, list);
        if (NULL == pnode) {
            infra_err("[%8s] list node invalid\n", plist_ctx->_token);
            ret = -1;
            goto EXIT;
        }
        __infra_list_giveback(plist_ctx, pnode);
    }

EXIT:
    pthread_mutex_unlock(&plist_ctx->_lock);

    return ret;
}

int32_t infra_list_set_depth(infra_list_handle handle, int32_t depth)
{
    infra_list_ctx_t *plist_ctx = NULL;
    infra_list_node_t *pnode = NULL;
    INFRA_PTR_CHECK(handle);

    plist_ctx = (infra_list_ctx_t *)handle;
    if (depth > plist_ctx->_max_size) {
        infra_err("[%8s] list max_size:%d, depth:%d invalid\n", plist_ctx->_token, plist_ctx->_max_size,
                  depth);
        return -1;
    }

    pthread_mutex_lock(&plist_ctx->_lock);
    plist_ctx->_depth = depth;
    while (plist_ctx->_data_count > depth) {
        pnode = infra_dl_list_first_entry(&plist_ctx->_list, infra_list_node_t, list);
        if (NULL == pnode) {
            infra_err("[%8s] list node invalid\n", plist_ctx->_token);
            continue;
        }
        __infra_list_giveback(plist_ctx, pnode);
    }
    pthread_mutex_unlock(&plist_ctx->_lock);

    return 0;
}

int32_t infra_list_get_depth(infra_list_handle handle, int32_t *depth)
{
    infra_list_ctx_t *plist_ctx = NULL;
    INFRA_PTR_CHECK(handle);
    INFRA_PTR_CHECK(depth);

    plist_ctx = (infra_list_ctx_t *)handle;
    *depth = plist_ctx->_depth;

    return 0;
}

#ifdef PUSH_INSERT_GIVEBACK_CB
int32_t infra_list_register_giveback_callback(infra_list_handle handle,
        pfn_infra_list_gb_cb callback, void *puserdata)
{
    infra_list_ctx_t *plist_ctx = NULL;
    INFRA_PTR_CHECK(handle);

    plist_ctx = (infra_list_ctx_t *)handle;

    if (NULL != plist_ctx->_pfn_giveback_cb || NULL != plist_ctx->_pgb_userdata) {
        infra_err("[%8s] list giveback callback registered\n", plist_ctx->_token);
        return -1;
    }

    pthread_mutex_lock(&plist_ctx->_lock);
    plist_ctx->_pfn_giveback_cb = callback;
    plist_ctx->_pgb_userdata = puserdata;
    pthread_mutex_unlock(&plist_ctx->_lock);

    return 0;
}

int32_t infra_list_unregister_giveback_callback(infra_list_handle handle)
{
    infra_list_ctx_t *plist_ctx = NULL;
    INFRA_PTR_CHECK(handle);

    plist_ctx = (infra_list_ctx_t *)handle;

    if ((NULL == plist_ctx->_pfn_giveback_cb) && (NULL == plist_ctx->_pgb_userdata)) {
        infra_err("[%8s] list giveback callback unregistered\n", plist_ctx->_token);
        return 0;
    }

    pthread_mutex_lock(&plist_ctx->_lock);
    plist_ctx->_pfn_giveback_cb = NULL;
    plist_ctx->_pgb_userdata = NULL;
    pthread_mutex_unlock(&plist_ctx->_lock);

    return 0;
}
#endif

int32_t infra_list_register_notify_callback(infra_list_handle handle, pfn_infra_list_nt_cb callback,
        void *puserdata)
{
    infra_list_ctx_t *plist_ctx = NULL;
    INFRA_PTR_CHECK(handle);

    plist_ctx = (infra_list_ctx_t *)handle;

    if (NULL != plist_ctx->_pfn_notify_cb || NULL != plist_ctx->_pnt_userdata) {
        infra_err("[%8s] list notify callback registered\n", plist_ctx->_token);
        return -1;
    }

    pthread_mutex_lock(&plist_ctx->_lock);
    plist_ctx->_pfn_notify_cb = callback;
    plist_ctx->_pnt_userdata = puserdata;
    pthread_mutex_unlock(&plist_ctx->_lock);

    return 0;
}

int32_t infra_list_unregister_notify_callback(infra_list_handle handle)
{
    infra_list_ctx_t *plist_ctx = NULL;
    INFRA_PTR_CHECK(handle);

    plist_ctx = (infra_list_ctx_t *)handle;

    if ((NULL == plist_ctx->_pfn_notify_cb) && (NULL == plist_ctx->_pnt_userdata)) {
        infra_err("[%8s] list notify callback unregistered\n", plist_ctx->_token);
        return 0;
    }

    pthread_mutex_lock(&plist_ctx->_lock);
    plist_ctx->_pfn_notify_cb = NULL;
    plist_ctx->_pnt_userdata = NULL;
    pthread_mutex_unlock(&plist_ctx->_lock);

    return 0;
}

int32_t infra_list_push_front(infra_list_handle handle, void *pdata)
{
    int32_t ret = 0;
    infra_list_ctx_t *plist_ctx = NULL;
    infra_list_node_t *pnode = NULL;
    INFRA_PTR_CHECK(handle);

    plist_ctx = (infra_list_ctx_t *)handle;

    pthread_mutex_lock(&plist_ctx->_lock);
    if (plist_ctx->_data_count >= plist_ctx->_depth) {
#ifndef PUSH_INSERT_GIVEBACK_CB
        infra_err("[%8s] list is full, size:%d\n", plist_ctx->_token, plist_ctx->_data_count);
        ret = -1;
        goto EXIT;
#else
        /* FIXME: how to decided to giveback front or back element */
        while (plist_ctx->_data_count >= plist_ctx->_depth) {
            pnode = infra_dl_list_first_entry(&plist_ctx->_list, infra_list_node_t, list);
            if (NULL == pnode) {
                infra_err("[%8s] list node invalid\n", plist_ctx->_token);
                ret = -1;
                goto EXIT;
            }

            __infra_list_giveback(plist_ctx, pnode);
        }
#endif
    }

    pnode = (infra_list_node_t *)infra_tb_get(plist_ctx->_tb_handle);
    if (NULL == pnode) {
        infra_err("[%8s] list node get failed\n", plist_ctx->_token);
        ret = -1;
        goto EXIT;
    }

    INFRA_INIT_LIST_HEAD(&pnode->list);
    pnode->data = pdata;
    infra_dl_list_add(&pnode->list, &plist_ctx->_list);
    plist_ctx->_data_count++;
    plist_ctx->_in_count++;

    if (plist_ctx->_pfn_notify_cb) {
        plist_ctx->_pfn_notify_cb(plist_ctx->_pnt_userdata, pdata);
    }

EXIT:
    pthread_mutex_unlock(&plist_ctx->_lock);

    if (plist_ctx->_is_block && (0 == ret)) {
        pthread_cond_broadcast(&plist_ctx->_cond);
    }

    return ret;
}

int32_t infra_list_push_back(infra_list_handle handle, void *pdata)
{
    int32_t ret = 0;
    infra_list_ctx_t *plist_ctx = NULL;
    infra_list_node_t *pnode = NULL;
    INFRA_PTR_CHECK(handle);

    plist_ctx = (infra_list_ctx_t *)handle;

    pthread_mutex_lock(&plist_ctx->_lock);
    if (plist_ctx->_data_count >= plist_ctx->_depth) {
#ifndef PUSH_INSERT_GIVEBACK_CB
        infra_err("[%8s] list is full, size:%d\n", plist_ctx->_token, plist_ctx->_data_count);
        ret = -1;
        goto EXIT;
#else
        /* FIXME: how to decided to giveback front or back element */
        while (plist_ctx->_data_count >= plist_ctx->_depth) {
            pnode = infra_dl_list_first_entry(&plist_ctx->_list, infra_list_node_t, list);
            if (NULL == pnode) {
                infra_err("[%8s] list node invalid\n", plist_ctx->_token);
                ret = -1;
                goto EXIT;
            }

            __infra_list_giveback(plist_ctx, pnode);
        }
#endif
    }

    pnode = (infra_list_node_t *)infra_tb_get(plist_ctx->_tb_handle);
    if (NULL == pnode) {
        infra_err("[%8s] list node get failed\n", plist_ctx->_token);
        ret = -1;
        goto EXIT;
    }

    INFRA_INIT_LIST_HEAD(&pnode->list);
    pnode->data = pdata;
    infra_dl_list_add_tail(&pnode->list, &plist_ctx->_list);
    plist_ctx->_data_count++;
    plist_ctx->_in_count++;

    if (plist_ctx->_pfn_notify_cb) {
        plist_ctx->_pfn_notify_cb(plist_ctx->_pnt_userdata, pdata);
    }

EXIT:
    pthread_mutex_unlock(&plist_ctx->_lock);
    if (plist_ctx->_is_block && (0 == ret)) {
        pthread_cond_broadcast(&plist_ctx->_cond);
    }

    return ret;
}

void *infra_list_pop_front(infra_list_handle handle, int32_t time_out)
{
    infra_list_ctx_t *plist_ctx = NULL;
    infra_list_node_t *pnode = NULL;
    void *pdata = NULL;
    struct timespec timeout = {0};

    if (NULL == handle) {
        infra_err("%s is NULL\n", TO_STRING(handle));
        return NULL;
    }

    plist_ctx = (infra_list_ctx_t *)handle;

    if (0 == plist_ctx->_data_count) {
        if (plist_ctx->_is_block && (time_out >= 0)) {
            if (0 == time_out) {
                __infra_list_cond_wait(plist_ctx);
            } else {
                __infra_list_cond_wait_timeout(plist_ctx, time_out);
            }
            if (0 == plist_ctx->_data_count) {
                infra_err("[%8s] list is emtpy\n", plist_ctx->_token);
                return NULL;
            }

        } else {
            infra_err("[%8s] list is emtpy\n", plist_ctx->_token);
            return NULL;
        }
    }

    pthread_mutex_lock(&plist_ctx->_lock);

    pnode = infra_dl_list_first_entry(&plist_ctx->_list, infra_list_node_t, list);
    if (NULL == pnode) {
        infra_err("[%8s] list node invalid\n", plist_ctx->_token);
        goto EXIT;
    }

    infra_dl_list_del_init(&pnode->list);
    pdata = pnode->data;

    infra_tb_giveback(plist_ctx->_tb_handle, (void *)pnode);

    plist_ctx->_data_count--;
    plist_ctx->_out_count++;

EXIT:
    pthread_mutex_unlock(&plist_ctx->_lock);

    return pdata;
}

void *infra_list_pop_back(infra_list_handle handle, int32_t time_out)
{
    infra_list_ctx_t *plist_ctx = NULL;
    infra_list_node_t *pnode = NULL;
    void *pdata = NULL;
    struct timespec timeout = {0};

    if (NULL == handle) {
        infra_err("%s is NULL\n", TO_STRING(handle));
        return NULL;
    }

    plist_ctx = (infra_list_ctx_t *)handle;

    if (0 == plist_ctx->_data_count) {
        if (plist_ctx->_is_block && (time_out >= 0)) {
            if (0 == time_out) {
                __infra_list_cond_wait(plist_ctx);
            } else {
                __infra_list_cond_wait_timeout(plist_ctx, time_out);
            }
            if (0 == plist_ctx->_data_count) {
                infra_err("[%8s] list is emtpy\n", plist_ctx->_token);
                return NULL;
            }

        } else {
            infra_err("[%8s] list is emtpy\n", plist_ctx->_token);
            return NULL;
        }
    }

    pthread_mutex_lock(&plist_ctx->_lock);
    pnode = infra_dl_list_last_entry(&plist_ctx->_list, infra_list_node_t, list);
    if (NULL == pnode) {
        infra_err("[%8s] list node invalid\n", plist_ctx->_token);
        goto EXIT;
    }

    infra_dl_list_del_init(&pnode->list);
    pdata = pnode->data;

    infra_tb_giveback(plist_ctx->_tb_handle, (void *)pnode);

    plist_ctx->_data_count--;
    plist_ctx->_out_count++;

EXIT:
    pthread_mutex_unlock(&plist_ctx->_lock);

    return pdata;
}

int32_t infra_list_insert_prev(infra_list_handle handle, void *pos_data, void *pdata)
{
    int32_t ret = 0;
    bool find_flg = false;
    infra_list_ctx_t *plist_ctx = NULL;
    infra_list_node_t *pos_node = NULL, *pnode = NULL;
    INFRA_PTR_CHECK(handle);

    plist_ctx = (infra_list_ctx_t *)handle;

    pthread_mutex_lock(&plist_ctx->_lock);
    if (0 == plist_ctx->_data_count) {
        infra_err("[%8s] list is empty\n", plist_ctx->_token);
        ret = -1;
        goto EXIT;
    }

    if (plist_ctx->_data_count >= plist_ctx->_depth) {
#ifndef PUSH_INSERT_GIVEBACK_CB
        infra_err("[%8s] list is full, size:%d\n", plist_ctx->_token, plist_ctx->_data_count);
        ret = -1;
        goto EXIT;
#else
        /* FIXME: how to decided to giveback front or back element */
        while (plist_ctx->_data_count >= plist_ctx->_depth) {
            pnode = infra_dl_list_first_entry(&plist_ctx->_list, infra_list_node_t, list);
            if (NULL == pnode) {
                infra_err("[%8s] list node invalid\n", plist_ctx->_token);
                ret = -1;
                goto EXIT;
            }

            __infra_list_giveback(plist_ctx, pnode);
        }
#endif
    }

    infra_dl_list_for_each_entry(pos_node, &plist_ctx->_list, list) {
        if (pos_data == pos_node->data) {
            find_flg = true;
            break;
        }
    }

    if (false == find_flg) {
        infra_err("[%8s] list pos_data find failed\n", plist_ctx->_token);
        ret = -1;
        goto EXIT;
    }

    pnode = (infra_list_node_t *)infra_tb_get(plist_ctx->_tb_handle);
    if (NULL == pnode) {
        infra_err("[%8s] list node get failed\n", plist_ctx->_token);
        ret = -1;
        goto EXIT;
    }

    INFRA_INIT_LIST_HEAD(&pnode->list);
    pnode->data = pdata;
    infra_dl_list_add_before(&pnode->list, &pos_node->list);
    plist_ctx->_data_count++;
    plist_ctx->_in_count++;

    if (plist_ctx->_pfn_notify_cb) {
        plist_ctx->_pfn_notify_cb(plist_ctx->_pnt_userdata, pdata);
    }

EXIT:
    pthread_mutex_unlock(&plist_ctx->_lock);
    if (plist_ctx->_is_block && (0 == ret)) {
        pthread_cond_broadcast(&plist_ctx->_cond);
    }

    return ret;
}

int32_t infra_list_insert_next(infra_list_handle handle, void *pos_data, void *pdata)
{
    int32_t ret = 0;
    bool find_flg = false;
    infra_list_ctx_t *plist_ctx = NULL;
    infra_list_node_t *pos_node = NULL, *pnode = NULL;
    INFRA_PTR_CHECK(handle);

    plist_ctx = (infra_list_ctx_t *)handle;

    pthread_mutex_lock(&plist_ctx->_lock);
    if (0 == plist_ctx->_data_count) {
        infra_err("[%8s] list is empty\n", plist_ctx->_token);
        ret = -1;
        goto EXIT;
    }

    if (plist_ctx->_data_count >= plist_ctx->_depth) {
#ifndef PUSH_INSERT_GIVEBACK_CB
        infra_err("[%8s] list is full, size:%d\n", plist_ctx->_token, plist_ctx->_data_count);
        ret = -1;
        goto EXIT;
#else
        /* FIXME: how to decided to giveback front or back element */
        while (plist_ctx->_data_count >= plist_ctx->_depth) {
            pnode = infra_dl_list_first_entry(&plist_ctx->_list, infra_list_node_t, list);
            if (NULL == pnode) {
                infra_err("[%8s] list node invalid\n", plist_ctx->_token);
                ret = -1;
                goto EXIT;
            }

            __infra_list_giveback(plist_ctx, pnode);
        }
#endif
    }

    infra_dl_list_for_each_entry(pos_node, &plist_ctx->_list, list) {
        if (pos_data == pos_node->data) {
            find_flg = true;
            break;
        }
    }

    if (false == find_flg) {
        infra_err("[%8s] list pos_data find failed\n", plist_ctx->_token);
        ret = -1;
        goto EXIT;
    }

    pnode = (infra_list_node_t *)infra_tb_get(plist_ctx->_tb_handle);
    if (NULL == pnode) {
        infra_err("[%8s] list node get failed\n", plist_ctx->_token);
        ret = -1;
        goto EXIT;
    }

    INFRA_INIT_LIST_HEAD(&pnode->list);
    pnode->data = pdata;
    infra_dl_list_add_next(&pnode->list, &pos_node->list);
    plist_ctx->_data_count++;
    plist_ctx->_in_count++;

    if (plist_ctx->_pfn_notify_cb) {
        plist_ctx->_pfn_notify_cb(plist_ctx->_pnt_userdata, pdata);
    }

EXIT:
    pthread_mutex_unlock(&plist_ctx->_lock);
    if (plist_ctx->_is_block && (0 == ret)) {
        pthread_cond_broadcast(&plist_ctx->_cond);
    }

    return ret;
}

int32_t infra_list_remove(infra_list_handle handle, void *pdata)
{
    int32_t ret = 0;
    bool find_flg = false;
    infra_list_ctx_t *plist_ctx = NULL;
    infra_list_node_t *pos_node = NULL;
    INFRA_PTR_CHECK(handle);

    plist_ctx = (infra_list_ctx_t *)handle;

    pthread_mutex_lock(&plist_ctx->_lock);
    if (0 == plist_ctx->_data_count) {
        infra_err("[%8s] list is empty\n", plist_ctx->_token);
        ret = -1;
        goto EXIT;
    }

    infra_dl_list_for_each_entry(pos_node, &plist_ctx->_list, list) {
        if (pdata == pos_node->data) {
            find_flg = true;
            break;
        }
    }

    if (false == find_flg) {
        infra_err("[%8s] list pdata find failed\n", plist_ctx->_token);
        ret = -1;
        goto EXIT;
    }

    infra_dl_list_del_init(&pos_node->list);

    if (plist_ctx->_pfn_giveback_cb) {
        plist_ctx->_pfn_giveback_cb(plist_ctx->_pgb_userdata, pos_node->data);
    }

    infra_tb_giveback(plist_ctx->_tb_handle, pos_node);

    plist_ctx->_data_count--;
    plist_ctx->_out_count++;

EXIT:
    pthread_mutex_unlock(&plist_ctx->_lock);

    return ret;
}

int32_t infra_list_travel(infra_list_handle handle, pfn_infra_list_travel_cb callback,
                          void *puserdata)
{
    int32_t ret = 0;
    infra_list_ctx_t *plist_ctx = NULL;
    infra_list_node_t *pos_node = NULL;
    INFRA_PTR_CHECK(handle);
    INFRA_PTR_CHECK(callback);

    plist_ctx = (infra_list_ctx_t *)handle;

    pthread_mutex_lock(&plist_ctx->_lock);
    if (0 == plist_ctx->_data_count) {
        infra_err("[%8s] list is empty\n", plist_ctx->_token);
        ret = -1;
        goto EXIT;
    }

    infra_dl_list_for_each_entry(pos_node, &plist_ctx->_list, list) {
        callback(puserdata, pos_node->data);
    }

EXIT:
    pthread_mutex_unlock(&plist_ctx->_lock);

    return ret;
}

void *infra_list_find(infra_list_handle handle, pfn_infra_list_find_cb callback, void *puserdata)
{
    int32_t ret = 0;
    bool find_flg = false;
    infra_list_ctx_t *plist_ctx = NULL;
    infra_list_node_t *pos_node = NULL;

    if (NULL == handle || NULL == callback) {
        infra_err("%s / callback is NULL\n", TO_STRING(handle));
        return NULL;
    }

    plist_ctx = (infra_list_ctx_t *)handle;

    pthread_mutex_lock(&plist_ctx->_lock);
    if (0 == plist_ctx->_data_count) {
        infra_err("[%8s] list is empty\n", plist_ctx->_token);
        ret = -1;
        goto EXIT;
    }

    infra_dl_list_for_each_entry(pos_node, &plist_ctx->_list, list) {
        find_flg = callback(puserdata, pos_node->data);
        if (find_flg) {
            break;
        }
    }

EXIT:
    pthread_mutex_unlock(&plist_ctx->_lock);

    if (false == find_flg) {
        infra_err("[%8s] list find failed\n", plist_ctx->_token);
        return NULL;
    }

    return pos_node->data;
}

void *infra_list_front(infra_list_handle handle)
{
    infra_list_ctx_t *plist_ctx = NULL;
    infra_list_node_t *pnode = NULL;
    void *pdata = NULL;

    if (NULL == handle) {
        infra_err("%s is NULL\n", TO_STRING(handle));
        return NULL;
    }

    plist_ctx = (infra_list_ctx_t *)handle;

    pthread_mutex_lock(&plist_ctx->_lock);
    if (0 == plist_ctx->_data_count) {
        infra_err("[%8s] list is emtpy\n", plist_ctx->_token);
        goto EXIT;
    }

    pnode = infra_dl_list_first_entry(&plist_ctx->_list, infra_list_node_t, list);
    if (NULL == pnode) {
        infra_err("[%8s] list node invalid\n", plist_ctx->_token);
        goto EXIT;
    }

    pdata = pnode->data;

EXIT:
    pthread_mutex_unlock(&plist_ctx->_lock);

    return pdata;
}

void *infra_list_back(infra_list_handle handle)
{
    infra_list_ctx_t *plist_ctx = NULL;
    infra_list_node_t *pnode = NULL;
    void *pdata = NULL;

    if (NULL == handle) {
        infra_err("%s is NULL\n", TO_STRING(handle));
        return NULL;
    }

    plist_ctx = (infra_list_ctx_t *)handle;

    pthread_mutex_lock(&plist_ctx->_lock);
    if (0 == plist_ctx->_data_count) {
        infra_err("[%8s] list is emtpy\n", plist_ctx->_token);
        goto EXIT;
    }

    pnode = infra_dl_list_last_entry(&plist_ctx->_list, infra_list_node_t, list);
    if (NULL == pnode) {
        infra_err("[%8s] list node invalid\n", plist_ctx->_token);
        goto EXIT;
    }
    pdata = pnode->data;

EXIT:
    pthread_mutex_unlock(&plist_ctx->_lock);

    return pdata;
}


int32_t infra_list_empty(infra_list_handle handle)
{
    infra_list_ctx_t *plist_ctx = NULL;
    INFRA_PTR_CHECK(handle);

    plist_ctx = (infra_list_ctx_t *)handle;

    return (plist_ctx->_data_count == 0) ? true : false;
}

int32_t infra_list_size(infra_list_handle handle)
{
    infra_list_ctx_t *plist_ctx = NULL;
    INFRA_PTR_CHECK(handle);

    plist_ctx = (infra_list_ctx_t *)handle;

    return plist_ctx->_data_count;
}

int32_t infra_list_get_stat(infra_list_handle handle, infra_list_stat_info_t *pstat_info)
{
    infra_list_ctx_t *plist_ctx = NULL;
    INFRA_PTR_CHECK(handle);
    INFRA_PTR_CHECK(pstat_info);

    plist_ctx = (infra_list_ctx_t *)handle;

    pstat_info->in_count = plist_ctx->_in_count;
    pstat_info->out_count = plist_ctx->_out_count;
    pstat_info->stash_count = plist_ctx->_data_count;

    return 0;
}